package gitlet;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class MergeUtils {

    public static String findLCA(String currentCommit, String otherCommit) {
        Set<String> allCA = findAllCA(currentCommit, otherCommit);
        Set<String> survivors = new HashSet<>(allCA);
        for (String sha1 : allCA) {
            String parent1 = Commit.fromFile(sha1).getParent();
            String parent2 = Commit.fromFile(sha1).getParent2();

            //移除所有的parent
            if (parent1 != null) {
                survivors.remove(parent1);
            }
            if (parent2 != null) {
                survivors.remove(parent2);
            }
        }
        Iterator<String> iterator = survivors.iterator();
        return iterator.next();

    }


    //出现conflict的时候，怎么处理文件,a.txt以及a.txtv1的sha1,a.txtv2的sha1
    public static void writeConflictFile(String fileName, String currentFileSha1, String otherFileSha1) {

        String s1 = "<<<<<<< HEAD\n";
        byte[] byteArrayCurr = new byte[0];
        if(currentFileSha1 != null){
            byteArrayCurr = Blob.fromFile(currentFileSha1).getData();
        }
        String s2 = "=======\n";
        byte[] byteArrayOth = new byte[0];
        if(otherFileSha1 != null){
            byteArrayOth = Blob.fromFile(otherFileSha1).getData();
        }
        String s3 = ">>>>>>>\n";
        File s = new File(Repository.CWD, fileName);
        if (!s.exists()) {
            try {
                s.createNewFile();
            } catch (IOException var4) {
                var4.printStackTrace();
            }
        }
        Utils.writeContents(s, s1, byteArrayCurr, s2, byteArrayOth, s3);


    }


    //如果LCA是其中一个commit
    public static void doFastForward(Repository repo, String branchName) {
        if (repo.checkout3(branchName) == 0) {
            repo.getBranches().put(repo.getHead(), repo.getBranches().get(branchName));
            repo.saveRepository();
        }
    }


    public static void handleMerge(Repository repo, String otherBranch) {


        String currentCommitID = repo.getBranches().get(repo.getHead());
        String otherCommitID = repo.getBranches().get(otherBranch);



        String lcaCommitID = findLCA(currentCommitID, otherCommitID);
        Commit currentCommit = Commit.fromFile(currentCommitID);
        Commit otherCommit = Commit.fromFile(otherCommitID);
        Commit lcaCommit = Commit.fromFile(lcaCommitID);


        if (Objects.equals(currentCommitID, lcaCommitID)) {
            System.out.println("Current branch fast-forwarded.");
            doFastForward(repo, otherBranch);
        } else if (!Objects.equals(currentCommitID, lcaCommitID) && !Objects.equals(otherCommitID, lcaCommitID)) {
            repo.makeMergeCommit(lcaCommit, currentCommit, otherCommit, repo.getHead(), otherBranch);
        } else if (Objects.equals(otherCommitID, lcaCommitID)) {
            System.out.println("Given branch is an ancestor of the current branch.");
        }

        repo.saveRepository();
    }

    public static Set<String> findAllCA(String currentCommit, String otherCommit) {
        Set<String> CACommits = new HashSet<>();

        Set<String> currentParentCommits = findAllCommitAncestors(currentCommit);
        Set<String> otherParentCommits = findAllCommitAncestors(otherCommit);

        //对比两个hashmap找到相同的元素放进去LCAcommits
        for (String parent : currentParentCommits) {
            if (otherParentCommits.contains(parent)) {
                CACommits.add(parent);
            }
        }
        return CACommits;
    }

    public static Set<String> findAllCommitAncestors(String currentCommit) {
        Deque<String> nodes = new ArrayDeque<>();
        Set<String> seenNodes = new HashSet<>();
        seenNodes.add(currentCommit);
        nodes.addFirst(currentCommit);
        while (!nodes.isEmpty()) {
            String currentNode = nodes.removeLast();
            List<String> currentParents = Commit.fromFile(currentNode).getParents();
            for (String parent : currentParents) {
                if (seenNodes.contains(parent)) {
                    continue;
                } else {
                    if (parent != null) {
                        seenNodes.add(parent);
                        nodes.addFirst(parent);
                    }
                }
            }

        }
        return seenNodes;
    }

}
